# frozen_string_literal: true

module Security
  module VulnerabilityScanning
    class CreateVulnerabilityService
      # Create a Vulnerabilities::Finding from an SBOM component, and vulnerability advisory.
      #
      # @param project [Project]
      # @param pipeline [Ci::Pipeline]
      # @param params [Hash]
      # @option params [::Gitlab::Ci::Reports::Sbom::Source, Sbom::Source] :sbom_source
      # @option params [::Gitlab::VulnerabilityScanning::Advisory] :advisory
      #
      # @return [ServiceResponse] If successful, the service response payload contains
      #   a `vulnerability_id` key with the id of the new vulnerability. Otherwise, the
      #   payload contains the error raised during execution.
      def self.execute(...)
        new(...).execute
      end

      def initialize(project:, pipeline:, params:)
        @project = project
        @pipeline = pipeline
        @sbom_source = params[:sbom_source]
        @advisories = params[:advisories]
      end

      def execute
        if pipeline.user.nil?
          raise ArgumentError,
            'Pipeline must have a corresponding user to use as vulnerability author'
        end

        vulnerability_ids = ::Security::Ingestion::IngestReportSliceService.execute(pipeline, finding_maps)
        ServiceResponse.success(message: "Vulnerabilities were created",
          payload: { vulnerability_ids: vulnerability_ids })
      rescue StandardError => error
        process_error(error)
      end

      private

      attr_reader :project, :pipeline, :sbom_source, :advisories

      def report_scanner
        ::Gitlab::VulnerabilityScanning::SecurityScanner.fabricate
      end

      def project_scanner
        ::Gitlab::VulnerabilityScanning::SecurityScanner.find_or_create_for_project!(project)
      end

      def finding(advisory)
        purl_type = advisory.affected_component.purl_type
        builder = ::Gitlab::VulnerabilityScanning::FindingBuilder.for_purl_type!(purl_type)
        builder.new(
          project: project,
          pipeline: pipeline,
          sbom_source: sbom_source,
          scanner: report_scanner,
          advisory: advisory
        ).finding
      end

      def finding_maps
        advisories.map { |advisory| FindingMap.new(finding(advisory), project_scanner.id) }
      end

      def process_error(error)
        ::Gitlab::ErrorTracking.track_exception(error, project_id: project.id)
        ServiceResponse.error(message: "Vulnerabilities were not created", payload: { error: error })
      end
    end
  end
end
